package com.hqyj.javaSpringBoot.modules.test.service.impl;

import com.hqyj.javaSpringBoot.modules.common.entity.Result;
import com.hqyj.javaSpringBoot.modules.common.entity.SearchBean;
import com.hqyj.javaSpringBoot.modules.test.dao.StudentRepository;
import com.hqyj.javaSpringBoot.modules.test.entity.Student;
import com.hqyj.javaSpringBoot.modules.test.service.StudentService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentRepository studentRepository;

    @Override
    public Result<Student> insertStudent(Student student) {
        studentRepository.saveAndFlush(student);
        return new Result<Student>(Result.ResultCode.SUCCESS.code,
                "Insert success", student);
    }

    @Override
    public Result<Student> editStudent(Student student) {
        studentRepository.saveAndFlush(student);
        return new Result<Student>(Result.ResultCode.SUCCESS.code,
                "Edit success", student);
    }

    @Override
    public Result<Object> deleteStudent(int Id) {
        studentRepository.deleteById(Id);
        return new Result<Object>(Result.ResultCode.SUCCESS.code,
                "Edit success");
    }

    @Override
    public Student getStudentById(int Id) {
        return studentRepository.getById(Id);
    }

    @Override
    public List<Student> getStudents() {
        return Optional.ofNullable(studentRepository.findAll())
                .orElse(Collections.emptyList());
    }

    @Override
    public Student findFirstByNameAndEmail(String name, String email) {
        return studentRepository.findFirstByNameAndEmail(name, email);
    }

    @Override
    public Student findFirstByAge(int age) {
        return studentRepository.findFirstByAge(age);
    }

    @Override
    public Page<Student> getStudentsBySearchBean(SearchBean searchBean) {
        searchBean.initSearchBean();
        return getStudentsBySpecification(searchBean);
    }

    private Page<Student> getStudentsByExample(SearchBean searchBean) {
        Sort.Direction direction = StringUtils.isBlank(searchBean.getDirection()) ||
                searchBean.getDirection().toLowerCase().equals("asc") ?
                Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, searchBean.getOrderBy());
        Pageable Pageable = PageRequest.of(
                searchBean.getCurrentPage() - 1,
                searchBean.getPageSize(),
                sort);

        Student student = new Student();
        student.setName(searchBean.getKeyword());
        student.setEmail(searchBean.getKeyword());
        ExampleMatcher matcher = ExampleMatcher
                .matchingAny()
                .withMatcher("name", match -> match.contains())
                .withMatcher("email", match -> match.contains());
        Example example = Example.of(student, matcher);
        return studentRepository.findAll(example, Pageable);
    }

    private Page<Student> getStudentsBySpecification(SearchBean searchBean) {
        Sort.Direction direction = StringUtils.isBlank(searchBean.getDirection()) ||
                searchBean.getDirection().toLowerCase().equals("asc") ?
                Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, searchBean.getOrderBy());
        Pageable Pageable = PageRequest.of(
                searchBean.getCurrentPage() - 1,
                searchBean.getPageSize(),
                sort);

        Specification<Student> specification = new Specification<Student>() {

            /**
             * select * from test_student where create_date > ? and
             * (id between 1 and 10 or name like '%?%'))
             * order by ? ? limit ?
             */
            @Override
            public Predicate toPredicate(Root<Student> root,
                                         CriteriaQuery<?> criteriaQuery,
                                         CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.and(
                        criteriaBuilder.greaterThan(
                                root.get("createDate").as(LocalDateTime.class),
                                LocalDateTime.of(2021, 8, 20, 0, 0)
                        ),
                        criteriaBuilder.or(
                                criteriaBuilder.between(root.get("id"), 1, 10),
                                criteriaBuilder.like(
                                        root.get("name").as(String.class),
                                        searchBean.getKeyword()
                                )
                        )
                );
            }
        };

        return studentRepository.findAll(specification, Pageable);
    }
}
